package gspringboot

import (
	"fmt"
	"gitee.com/hongzhaomin/ginplus"
	"gitee.com/hongzhaomin/gspring/annotation"
	"gitee.com/hongzhaomin/gspring/aware"
	"gitee.com/hongzhaomin/gspring/iface"
	"gitee.com/hongzhaomin/gspring/tools"
	"gitee.com/hongzhaomin/hzm-common-go/strutil/color"
	"github.com/gin-gonic/gin"
	"net/http"
	"reflect"
	"strconv"
	"time"
)

var _ aware.BeanFactoryAware = (*GinWebApplicationServer)(nil)

type GinWebApplicationServer struct {
	annotation.Component
	aware.Aware
	annotation.Initialize

	beanFactory iface.BeanFactory
	logger      iface.GspringLogger
	environment ConfigurationEnvironment
	webCtx      *ginplus.WebContext
}

func (my *GinWebApplicationServer) SetBeanFactory(factory iface.BeanFactory) {
	my.beanFactory = factory
	my.logger = factory.GetBeanByName(LoggerBeanName).(iface.GspringLogger)
	my.environment = factory.GetBeanByName(EnvironmentBeanName).(ConfigurationEnvironment)
}

func (my *GinWebApplicationServer) Init() {
	// 创建路由对象
	gin.SetMode(gin.ReleaseMode)
	// todo 等gspring支持了@ConditionalOnMissingBean、@ConditionalOnProperty功能，再将engine的注入能力给到用户
	engine := gin.New()
	engine.Use(my.customizeGinLogger(gin.LoggerConfig{}), gin.Recovery())

	// gin服务配置
	ginEngineConfigs := my.beanFactory.GetBeans(reflect.TypeOf((*GinEngineConfig)(nil)))
	for _, config := range ginEngineConfigs {
		config.(GinEngineConfig).Set(engine)
	}

	ginTempCnfs := my.beanFactory.GetBeans(reflect.TypeOf((*GinWebTempConfig)(nil)))
	if len(ginTempCnfs) > 0 {
		ginTempCnf := ginTempCnfs[0].(GinWebTempConfig)
		ginTempCnf.LoadHTMLGlob(engine.LoadHTMLGlob)
		ginTempCnf.LoadHTMLFiles(engine.LoadHTMLFiles)
		ginTempCnf.Static(func(relativePath, root string) {
			engine.Static(relativePath, root)
		})
		ginTempCnf.StaticFS(func(relativePath string, fs http.FileSystem) {
			engine.StaticFS(relativePath, fs)
		})
		ginTempCnf.StaticFile(func(relativePath, filepath string) {
			engine.StaticFile(relativePath, filepath)
		})
	}

	// 创建web容器
	ctx := ginplus.NewWebContext(engine)
	ctx.SetLogger(my.logger)
	my.setPropIfExist(gspringWebServerPathPropName, func(serverPath string) {
		ctx.SetContextPath(serverPath)
	})
	my.setPropIfExist(gspringWebServerPortPropName, func(serverPort string) {
		ctx.SetServerPort(serverPort)
	})
	my.setPropIfExist(gspringWebRespBuiltinPropName, func(isBuiltinStr string) {
		isBuiltin, _ := strconv.ParseBool(isBuiltinStr)
		ctx.SetUseBuiltinRespStruct(isBuiltin)
	})
	httpSession := my.beanFactory.GetBeanByType(ginplus.DefaultHttpSession{}).(ginplus.HttpSession)
	ctx.SetHttpSession(httpSession)
	my.webCtx = ctx
}

func (my *GinWebApplicationServer) StartHttpServer() {
	// 打印banner
	new(ginplusBanner).PrintBanner(my.environment, my.logger)
	my.registerConfiguration()
	// 异步启动http服务
	my.webCtx.StartHttpServerAsync()
}

func (my *GinWebApplicationServer) Shutdown() {
	my.webCtx.Shutdown()
}

func (my *GinWebApplicationServer) setPropIfExist(key string, set func(val string)) {
	environment := my.environment
	if environment.ContainsKey(key) {
		set(environment.GetStringReplacePlaceholders(key))
	}
}

func (my *GinWebApplicationServer) registerConfiguration() {
	logger := my.logger
	factory := my.beanFactory
	// 注册 [ginplus.Controller]
	ctrleans := factory.GetBeans(reflect.TypeOf((*ginplus.Controller)(nil)))
	for _, ctrlBean := range ctrleans {
		my.webCtx.AddControllers(ctrlBean.(ginplus.Controller))
	}

	// 注册 [ginplus.RestController]
	restCtrlBeans := factory.GetBeans(reflect.TypeOf((*ginplus.RestController)(nil)))
	for _, restCtrlBean := range restCtrlBeans {
		my.webCtx.AddControllers(restCtrlBean.(ginplus.RestController))
	}

	// 注册 [ginplus.GlobalPanicHandler]
	handlerBeans := factory.GetBeans(reflect.TypeOf((*ginplus.GlobalPanicHandler)(nil)))
	handlerCnt := len(handlerBeans)
	if handlerCnt == 1 {
		globalPanicHandler := handlerBeans[0].(ginplus.GlobalPanicHandler)
		my.webCtx.SetGlobalPanicHandler(globalPanicHandler)
	} else if handlerCnt > 1 {
		globalPanicHandler := handlerBeans[0].(ginplus.GlobalPanicHandler)
		logger.Warn("The GlobalPanicHandler type has %d instances, and named [%s] has been selected",
			handlerCnt, tools.GetFullPathName(globalPanicHandler))
		my.webCtx.SetGlobalPanicHandler(globalPanicHandler)
	}

	// 注册映射处理函数调用环绕通知处理器 [ginplus.MappingHandlerProcessor]
	httpHandler := NewConfigurationHttpHandler(my.webCtx)
	processorBeans := factory.GetBeans(reflect.TypeOf((*ginplus.MappingHandlerProcessor)(nil)))
	for _, processorBean := range processorBeans {
		processor := processorBean.(ginplus.MappingHandlerProcessor)
		httpHandler.processors = append(httpHandler.processors, processor)
	}
	if len(processorBeans) > 0 {
		logger.Info("The %d MappingHandlerProcessor have completed registration", len(processorBeans))
	}

	my.webCtx.SetHttpHandler(httpHandler)

	// 注册过滤器 [ginplus.Filter]
	filterBeans := factory.GetBeans(reflect.TypeOf((*ginplus.Filter)(nil)))
	for _, filterBean := range filterBeans {
		my.webCtx.AddFilters(filterBean.(ginplus.Filter))
	}
	if len(filterBeans) > 0 {
		logger.Info("The %d Filter have completed registration", len(filterBeans))
	}
}

func (my *GinWebApplicationServer) customizeGinLogger(conf gin.LoggerConfig) gin.HandlerFunc {
	formatter := conf.Formatter
	if formatter == nil {
		formatter = my.ginLogFmtFun
	}

	notlogged := conf.SkipPaths

	var skip map[string]struct{}

	if length := len(notlogged); length > 0 {
		skip = make(map[string]struct{}, length)

		for _, path := range notlogged {
			skip[path] = struct{}{}
		}
	}

	return func(c *gin.Context) {
		// Start timer
		start := time.Now()
		path := c.Request.URL.Path
		raw := c.Request.URL.RawQuery

		// Process request
		c.Next()

		// Log only when path is not being skipped
		if _, ok := skip[path]; !ok {
			param := gin.LogFormatterParams{
				Request: c.Request,
				//isTerm:  isTerm,
				Keys: c.Keys,
			}

			// Stop timer
			param.TimeStamp = time.Now()
			param.Latency = param.TimeStamp.Sub(start)

			param.ClientIP = c.ClientIP()
			param.Method = c.Request.Method
			param.StatusCode = c.Writer.Status()
			param.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String()

			param.BodySize = c.Writer.Size()

			if raw != "" {
				path = path + "?" + raw
			}

			param.Path = path

			my.logger.Println(formatter(param))
		}
	}
}

func (my *GinWebApplicationServer) ginLogFmtFun(param gin.LogFormatterParams) string {
	statusColor := param.StatusCodeColor()
	methodColor := param.MethodColor()
	resetColor := param.ResetColor()

	if param.Latency > time.Minute {
		param.Latency = param.Latency.Truncate(time.Second)
	}

	level := "INFO"
	levelColor := color.GreenTextHighlight
	if param.ErrorMessage != "" {
		level = "ERROR"
		levelColor = color.RedTextHighlight
	}

	msg := fmt.Sprintf("%v - %s%-5s%s --- [%s%30s%s] |%s %3d %s| %13v | %15s |%s %-7s %s %#v",
		param.TimeStamp.Format("2006-01-02 15:04:05.000"),
		levelColor, level, resetColor,
		color.CyanText, "GIN", resetColor,
		statusColor, param.StatusCode, resetColor,
		param.Latency,
		param.ClientIP,
		methodColor, param.Method, resetColor,
		param.Path,
	)
	if param.ErrorMessage != "" {
		return fmt.Sprintf("%s\n%s", msg, param.ErrorMessage)
	}
	return msg

	//return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %#v\n%s",
	//	param.TimeStamp.Format("2006/01/02 - 15:04:05"),
	//	statusColor, param.StatusCode, resetColor,
	//	param.Latency,
	//	param.ClientIP,
	//	methodColor, param.Method, resetColor,
	//	param.Path,
	//	param.ErrorMessage,
	//)
}

// ==================================================================================================
// =========================== 自定义http处理器 ConfigurationHttpHandler ==============================
// ==================================================================================================
var _ ginplus.HttpHandler = (*ConfigurationHttpHandler)(nil)

func NewConfigurationHttpHandler(webCtx *ginplus.WebContext) *ConfigurationHttpHandler {
	httpHandler := &ConfigurationHttpHandler{}
	httpHandler.CommonHttpHandler = ginplus.NewCommonHttpHandler(webCtx, httpHandler)
	return httpHandler
}

type ConfigurationHttpHandler struct {
	*ginplus.CommonHttpHandler
	processors []ginplus.MappingHandlerProcessor
}

func (my *ConfigurationHttpHandler) BeforeInvoke(ctx *gin.Context, mapping *ginplus.HandlerMapping, rvParams []reflect.Value) bool {
	wrapper := ginplus.NewHandlerMappingWrapper(mapping, ctx, rvParams)
	for _, processor := range my.processors {
		if pass := processor.Before(wrapper); !pass {
			return false
		}
	}
	return true
}

func (my *ConfigurationHttpHandler) AfterInvoke(ctx *gin.Context, mapping *ginplus.HandlerMapping, rvParams []reflect.Value, result any) bool {
	wrapper := ginplus.NewHandlerMappingWrapper(mapping, ctx, rvParams)
	for _, processor := range my.processors {
		if pass := processor.After(wrapper, result); !pass {
			return false
		}
	}
	return true
}
